package com.opencee.cloud.bpm.controller;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.opencee.cloud.bpm.api.IBpmProcessApi;
import com.opencee.cloud.bpm.service.ProcessEngineService;
import com.opencee.cloud.bpm.vo.*;
import com.opencee.common.exception.BaseFailException;
import com.opencee.common.model.ApiResult;
import com.opencee.common.model.PageResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.DelegationState;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @author yadu
 */
@Api(value = "流程API接口", tags = "流程API接口")
@RestController
public class BpmProcessApiController implements IBpmProcessApi {
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private ProcessEngineService processEngineService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private FormService formService;

    /**
     * 启动流程
     * 开启一个新的流程
     *
     * @param businessKey 业务标识
     * @param processKey  流程定义key
     * @param startUserId 流程提交，用户编码
     * @param formData    业务数据JSON字符串{“a”:”1”}
     * @return
     */
    @ApiOperation(value = "启动流程", notes = "开启一个新的流程")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "businessKey", value = "业务标识", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "processKey", value = "流程定义key", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "startUserId", value = "流程提交，用户编码", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "formData", value = "业务数据JSON字符串{“a”:”1”}", required = true, paramType = "form")
            }
    )
    @PostMapping("/process/start")
    @Override
    public ApiResult<BpmProcessResultVO> start(
            @RequestParam("businessKey") String businessKey,
            @RequestParam("processKey") String processKey,
            @RequestParam("startUserId") String startUserId,
            @RequestParam("formData") String formData) {
        JSONObject variables = JSONObject.parseObject(formData);
        ProcessInstance processInstance = processEngineService.startupProcessByKey(processKey, businessKey, startUserId, variables, "");
        String startFormKey = formService.getStartFormKey(processInstance.getProcessDefinitionId());
        BpmProcessResultVO result = new BpmProcessResultVO();
        result.setProcessInstId(processInstance.getId());
        result.setProcessKey(processInstance.getProcessDefinitionKey());
        result.setProcessName(processInstance.getProcessDefinitionName());
        result.setProcessVersion(processInstance.getProcessDefinitionVersion());
        result.setStartFormKey(startFormKey);
        result.setTenantId("");
        return ApiResult.ok().data(result);
    }

    /**
     * 暂停流程
     * 暂停/挂起流程, 暂停后流程将被锁定将不能提交任务，并提示错误。
     *
     * @param processInstId 流程实例编号
     * @return
     */
    @ApiOperation(value = "暂停流程", notes = "暂停/挂起流程, 暂停后流程将被锁定将不能提交任务，并提示错误。")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processId", value = "流程实例编号", required = true, paramType = "form")
            }
    )
    @PostMapping("/process/suspend")
    @Override
    public ApiResult suspend(
            @RequestParam("processInstId") String processInstId) {
        runtimeService.suspendProcessInstanceById(processInstId);
        return ApiResult.ok();
    }

    /**
     * 根据业务标识暂停流程
     * 暂停/挂起流程, 暂停后流程将被锁定将不能提交任务，并提示错误。
     *
     * @param businessKey 业务唯一标识
     * @return
     */
    @ApiOperation(value = "根据业务标识暂停流程", notes = "暂停/挂起流程, 暂停后流程将被锁定将不能提交任务，并提示错误。")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processKey", value = "流程定义标识", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "businessKey", value = "业务唯一标识", required = true, paramType = "form")
            }
    )
    @PostMapping("/process/suspend/business-key")
    @Override
    public ApiResult suspendByBusinessKey(@RequestParam("processKey") String processKey, @RequestParam("businessKey") String businessKey) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey, processKey).singleResult();
        runtimeService.suspendProcessInstanceById(processInstance.getProcessInstanceId());
        return ApiResult.ok();
    }

    /**
     * 激活流程
     * 恢复/激活流程，可以继续提交任务
     *
     * @param processInstId 流程实例编号
     * @return
     */
    @ApiOperation(value = "激活流程", notes = "恢复/激活流程，可以继续提交任务")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processId", value = "流程实例编号", required = true, paramType = "form")
            }
    )
    @PostMapping("/process/activate")
    @Override
    public ApiResult activate(
            @RequestParam("processInstId") String processInstId) {
        runtimeService.activateProcessInstanceById(processInstId);
        return ApiResult.ok();
    }

    /**
     * 根据业务标识激活流程
     * 恢复/激活流程，可以继续提交任务
     *
     * @param businessKey 业务唯一标识
     * @return
     */
    @ApiOperation(value = "根据业务标识激活流程", notes = "恢复/激活流程，可以继续提交任务")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processKey", value = "流程定义标识", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "businessKey", value = "业务唯一标识", required = true, paramType = "form")
            }
    )
    @PostMapping("/process/activate/business")
    @Override
    public ApiResult activateByBusinessKey(@RequestParam("processKey") String processKey, @RequestParam("businessKey") String businessKey) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey, processKey).singleResult();
        runtimeService.activateProcessInstanceById(processInstance.getProcessInstanceId());
        return ApiResult.ok();
    }

    /**
     * 终止流程
     * 终止/取消/删除流程
     *
     * @param processInstId 流程实例编号
     * @param startUserId   启动用户编号,可以为空.不为空将校验流程启动人和撤销人是否一致。
     * @param reason        备注说明
     * @return
     */
    @ApiOperation(value = "终止流程", notes = "终止/取消/删除流程")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processId", value = "流程实例编号", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "startUserId", value = "启动用户编号,可以为空.不为空将校验流程启动人和撤销人是否一致。", required = false, paramType = "form"),
                    @ApiImplicitParam(name = "reason", value = "备注说明", required = false, paramType = "form")
            }
    )
    @PostMapping("/process/stop")
    @Override
    public ApiResult stop(
            @RequestParam("processInstId") String processInstId,
            @RequestParam(value = "startUserId", required = false) String startUserId,
            @RequestParam(value = "reason", required = false) String reason) {
        if (!StringUtils.isEmpty(startUserId)) {
            ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstId).singleResult();
            if (instance == null) {
                return ApiResult.failed().msg("流程不存在或已完成");
            }
            if (!instance.getStartUserId().equals(startUserId)) {
                return ApiResult.failed().msg("非启动用户,无法撤销!");
            }
        }
        runtimeService.deleteProcessInstance(processInstId, reason);
        return ApiResult.ok();
    }

    /**
     * 根据业务标识终止流程
     * 终止/取消/删除流程
     *
     * @param businessKey 业务标识
     * @param reason      备注说明
     * @return
     */
    @ApiOperation(value = "根据业务标识终止流程", notes = "终止/取消/删除流程")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processKey", value = "流程定义标识", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "businessKey", value = "业务唯一标识", required = true, paramType = "form")
            }
    )
    @PostMapping("/process/shutdown/business-key")
    @Override
    public ApiResult shutdownByBusinessKey(@RequestParam("processKey") String processKey,
                                           @RequestParam("businessKey") String businessKey,
                                           @RequestParam(value = "reason", required = false) String reason) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey, processKey).singleResult();
        runtimeService.deleteProcessInstance(processInstance.getProcessInstanceId(), reason);
        return ApiResult.ok();
    }

    /**
     * 执行任务
     * 执行/提交任务,并添加批注
     *
     * @param taskId    任务编号
     * @param userId    任务提交人编号
     * @param operation 操作,查看:agree,refuse,return BpmConstants.class
     * @param comment   批注内容
     * @param formData  表单数据JSON字符串{“a”:”1”}
     * @return
     */
    @ApiOperation(value = "执行任务", notes = "执行/提交任务,并添加批注")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "taskId", value = "任务编号", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "userId", value = "任务提交人编号", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "operation", value = "操作,查看:agree,refuse,return", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "comment", value = "批注内容", required = false, paramType = "form"),
                    @ApiImplicitParam(name = "formData", value = "表单数据JSON字符串{“a”:”1”}", required = false, paramType = "form"),
            }
    )
    @PostMapping("/task/execute")
    @Override
    public ApiResult<BpmTaskResultVO> completeTask(
            @RequestParam("taskId") String taskId,
            @RequestParam("userId") String userId,
            @RequestParam(value = "operation") String operation,
            @RequestParam(value = "comment", required = false) String comment,
            @RequestParam(value = "formData", required = false) String formData) {
        JSONObject variables = null;
        if (StringUtils.hasText(formData)) {
            variables = JSONObject.parseObject(formData);
        } else {
            variables = new JSONObject();
        }
        variables.put("_OPT", operation);
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new BaseFailException("任务已结束或不存在");
        }
        //利用任务对象，查询流程实例id
        String processInstancesId = task.getProcessInstanceId();
        //由于流程用户上下文对象是线程独立的，所以要在需要的位置设置，要保证设置和查询操作在同一个线程中
        //批注人的名称  一定要写，不然查看的时候不知道人物信息
        Authentication.setAuthenticatedUserId(userId);
        taskService.addComment(taskId, processInstancesId, operation, comment);
        if (task.getDelegationState() != null && task.getDelegationState().ordinal() == DelegationState.PENDING.ordinal()) {
            // 委派人执行任务
            processEngineService.resolveTask(task.getId(), variables);
        } else {
            //执行任务
            processEngineService.completeTask(task.getId(), variables);
        }
        BpmTaskResultVO submitTaskResult = new BpmTaskResultVO();
        BpmTaskInfoVO currentTask = new BpmTaskInfoVO();
        currentTask.setTaskId(task.getId());
        currentTask.setTaskName(task.getName());
        currentTask.setTaskKey(task.getTaskDefinitionKey());
        currentTask.setAssignee(task.getAssignee());
        if (task instanceof UserTask) {
            UserTask userTask = (UserTask) task;
            currentTask.setCandidateGroups(userTask.getCandidateGroups());
            currentTask.setCandidateUsers(userTask.getCandidateUsers());
        }

        BpmTaskInfoVO nextTask = new BpmTaskInfoVO();
        // 下一任务
        FlowElement flowElement = processEngineService.getNextUserFlowElement(task);
        if (flowElement != null && flowElement instanceof Task) {
            Task next = (Task) flowElement;
            nextTask.setTaskId(next.getId());
            nextTask.setTaskName(next.getName());
            nextTask.setTaskKey(next.getTaskDefinitionKey());
            nextTask.setAssignee(next.getAssignee());
            if (flowElement instanceof UserTask) {
                UserTask userTask = (UserTask) flowElement;
                nextTask.setCandidateGroups(userTask.getCandidateGroups());
                nextTask.setCandidateUsers(userTask.getCandidateUsers());
            }
        }
        submitTaskResult.setNextTask(nextTask);
        return ApiResult.ok();
    }

    /**
     * 通过任务编号执行任务
     * 执行/提交任务,并添加批注
     *
     * @param processInstId 流程实例Id
     * @param taskKey       任务key
     * @param userId        任务提交人编号
     * @param operation     操作,查看:agree,refuse,return BpmConstants.class
     * @param comment       批注内容
     * @param formData      表单数据JSON字符串{“a”:”1”}
     * @return
     */
    @ApiOperation(value = "通过任务编号执行任务", notes = "执行/提交任务,并添加批注")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processInstId", value = "任务编号", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "taskKey", value = "任务key", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "userId", value = "任务提交人编号", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "operation", value = "操作,查看:agree,refuse,return", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "comment", value = "批注内容", required = false, paramType = "form"),
                    @ApiImplicitParam(name = "formData", value = "表单数据JSON字符串{“a”:”1”}", required = false, paramType = "form"),
            }
    )
    @PostMapping("/task/execute/key")
    @Override
    public ApiResult<BpmTaskResultVO>completeTaskByTaskKey(
            @RequestParam("processInstId") String processInstId,
            @RequestParam("taskKey") String taskKey,
            @RequestParam("userId") String userId,
            @RequestParam(value = "operation") String operation,
            @RequestParam(value = "comment", required = false) String comment,
            @RequestParam(value = "formData", required = false) String formData
    ) {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstId).taskDefinitionKey(taskKey).orderByTaskId().desc().list();
        if (taskList == null || taskList.isEmpty()) {
            throw new BaseFailException("任务已结束或不存在");
        }
        Task task = taskList.get(0);
        return completeTask(task.getId(), userId, operation, comment, formData);
    }


    /**
     * 任务委派
     * 将任务委派给另外一个受理人
     *
     * @param taskId 任务编号
     * @param userId 委托人编号
     * @return
     */
    @ApiOperation(value = "任务委派", notes = "将任务委派给另外一个受理人")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "taskId", value = "任务编号", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "userId", value = "委托人编号", required = true, paramType = "form"),
            }
    )
    @PostMapping("/task/delegate")
    @Override
    public ApiResult delegate(@RequestParam("taskId") String taskId,
                              @RequestParam("userId") String userId) {
        taskService.delegateTask(taskId, userId);
        return ApiResult.ok();
    }

    /**
     * 查询流程批注过程列表
     *
     * @param processId 流程实例编号
     * @return
     */
    @ApiOperation(value = "查询流程批注过程列表", notes = "查询流程批注过程列表")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "processId", value = "流程实例编号", required = true, paramType = "form"),
                    @ApiImplicitParam(name = "taskId", value = "任务定义标识", required = false, paramType = "form")
            }
    )
    @PostMapping(value = "/process/history/comments")
    public ApiResult<PageResult<BpmCommentVO>> historyComments(
            @RequestParam("processId") String processId,
            @RequestParam(value = "taskId", required = false) String taskId) {
        List<Comment> list = new ArrayList<>();
        if (!StringUtils.isEmpty(taskId)) {
            list = taskService.getTaskComments(taskId);
        } else {
            list = taskService.getProcessInstanceComments(processId);
        }
        List<BpmCommentVO> records = Lists.newArrayList();
        list.forEach(comment -> {
            BpmCommentVO target = new BpmCommentVO();
            HistoricTaskInstance hti = historyService.createHistoricTaskInstanceQuery()
                    .taskId(comment.getTaskId())
                    .singleResult();
            if (hti != null) {
                target.setTaskName(hti.getName());
            }
            target.setCommentId(comment.getId());
            target.setTaskId(comment.getTaskId());
            target.setUserId(comment.getUserId());
            target.setOperation(comment.getType());
            target.setComment(comment.getFullMessage());
            target.setCreateTime(comment.getTime());
            target.setProcessInstanceId(comment.getProcessInstanceId());
            records.add(target);
        });
        long count = Long.valueOf(list.size());
        PageResult<BpmCommentVO> page = new PageResult();
        page.setRecords(records);
        page.setTotal((int) count);
        return ApiResult.ok().data(page);
    }


}
